#ifndef __M_UTIL_H__
#define __M_UTIL_H__
#include "Log.hpp"
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include<fstream>
#include <mysql/mysql.h>
#include <jsoncpp/json/json.h>

#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
//将服务器对象重命名方便使用
typedef websocketpp::server<websocketpp::config::asio> wsserver_t;

// 这个头文件用来实现多个实用程序的类,工具类：不用实例化对象直接调用方法的类，说明类内是没变量的，只有对应的功能函数
class mysql_util
{
public:
    static MYSQL *mysql_create(const std::string &password, const std::string &dbname, const std::string &host, const std::string &user, const uint16_t port = 3306)
    {
        // 创建mysql句柄
        MYSQL *mysql = mysql_init(nullptr);
        if (mysql == nullptr)
        {
            ELOG("mysql init failed");
            return nullptr;
        }
        ILOG("mysql init successed");
        // 连接mysql服务器
        if (mysql_real_connect(mysql, host.c_str(), user.c_str(), password.c_str(), dbname.c_str(), port, NULL, 0) == nullptr)
        {
            // 后面两个可以调用error函数的原因就是这时mysql已经初始化好了
            ELOG("mysql connect failed : %s", mysql_error(mysql));
            mysql_close(mysql);
            return nullptr;
        }
        ILOG("mysql connect successed");
        // 设置客户端字符集 ==0是成功
        if (mysql_set_character_set(mysql, "utf8") != 0)
        {
            ELOG("mysql set character failed,%s", mysql_error(mysql));
            mysql_close(mysql);
            return nullptr;
        }
        ILOG("mysql set character successed");
        return mysql;
    }
    // 让mysql运行起来
    static bool mysql_exec(MYSQL *mysql, const std::string &sql)
    {
        if (mysql_query(mysql, sql.c_str()) != 0)
        {
            // 打印是哪个语句出错了
            ELOG("%s\n", sql.c_str());
            ELOG("mysql query failed : %s", mysql_error(mysql));
            return false;
        }
        return true;
    }
    // 关闭mysql
    static void mysql_destroy(MYSQL *mysql)
    {
        // 对于mysql关闭等一定要判空不然就是野指针
        if (mysql != nullptr)
            mysql_close(mysql);
        ILOG("mysql closed");
        return;
    }
};
class json_util
{
public:
    // 两个功能序列化和反序列化
    // root是一个传入传出参数
    // 序列化是把root中的值转为字符串
    static bool serialize(Json::Value &root, std::string &str)
    {
        Json::StreamWriterBuilder swb;
        // 智能指针的模版参数放的就是要管理的指针类型，swb类似于一个句柄可以用来new一个sw。sw是指针名字
        std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
        std::stringstream ss;
        if (sw->write(root, &ss) != 0)
        {
            ELOG("json serialize failed");
            return false;
        }
        // 将stringstream中的字符串取出放到str中。

        str = ss.str();
        return true;
    }
    static bool unserialize(Json::Value &root, std::string &str)
    {
        // 反序列化就是把字符串转换为Json value
        Json::CharReaderBuilder crb;
        std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
        std::string error;
        // 一般传指针的都是传入传出参数
        if (cr->parse(str.c_str(), str.c_str() + str.size(), &root, &str) == false)
        {
            ELOG("Json unserialize failed");
            return false;
        }
        return true;
    }
};

class str_util
{
public:
    // 用于根据分隔符分割指定的字符串，
    static int spilt(const std::string src, const std::string sep, std::vector<std::string> &vs)
    {
        // 1.找到sep，2.截取前面的字符串 3.放到vs中
        size_t index = 0, pos = 0;
        while (index < src.size())
        {

            pos = src.find(sep, index); // find的第一个参数是要找的字符串，第二个参数是从哪里开始找
            //如果没找到，有两种情况1.本来就没有分隔符，那就直接返回。如果分割了一部分，最后的一个字符串后并没有分隔符，所以此时将最后一个字符串放入vs中
            if (pos == std::string::npos)
            {
                // 没找到，index代表了从这个位置到最后都截取
                vs.push_back(src.substr(index));
                break;
            }
            // 找到位置后，更新index,截取字符串,如果pos==index，说明此时分隔符重复
            if(pos == index) 
            {
                //重复了那下一次就去下一个位置找
                index += sep.size();
                continue;
            }
            vs.push_back(src.substr(index, pos - index));
            //因为每次要跳过的是分隔符的长度
            index = pos + sep.size();
        }
        return vs.size();
    }   
};

class file_util
{
public:
    //对html读取的功能所以是二进制
    static bool read(const std::string &filename,std::string& ret)
    {
        //1.创建流
        //用二进制的方法打开filename
        std::ifstream ifs(filename,std::ios::binary);
        if(ifs.is_open() == false)
        {
            //打开失败
            ELOG("finame:%s open file failed!",filename.c_str());
            return false;
        }
        //2.读取文件
        //将文件定位到文件的末尾
        size_t len = 0;
        ifs.seekg(0,std::ios::end);
        //tellg用来获取当前在文件的位置，因为文件开头为0，所以最后的位置也就是文件长度了
        len = ifs.tellg();
        ifs.seekg(0,std::ios::beg);
        //将文件内容写入ret中
        ret.resize(len);//变为文件大小
        //c_str 是 const 修饰所以这里要直接找到首地址。
        ifs.read(&ret[0],len);
        //检查流是否正常
        if(ifs.good() == false)
        {
            //文件流异常
            ELOG("read %s file ifstream abnormal!",filename.c_str());
            ifs.close();
            return false;
        }
        
        //3.关闭流
        ifs.close();
        return true;
    }
};
#endif